package com.member.server.domain.dto;

import com.alibaba.fastjson.JSON;
import com.member.server.entity.*;
import com.member.server.enums.RecordStatus;
import com.member.server.enums.RecordType;

import java.util.*;

/**
 * @author gaojx1993
 * @version 1.0
 * @description AccountPayDTO
 * @since 2023/7/11 16:50
 */
public class AccPayDTO extends AccOpDTO {

    private final Long payAmount;

    private final List<Account> accounts;

    private Map<Long, Long> deductInfo;

    private List<RecordAccountPay> recordAccountPays;

    public AccPayDTO(Member member, List<Account> accounts, Long payAmount) {
        super(member);
        this.payAmount = payAmount;
        this.accounts = accounts;
        init();
    }

    @Override
    public void init() {
        // 生成扣款信息
        genDeductInfo();
        // 生成支付流水
        genRecord();
        // 生成支付流水详情
        genAccountPays();
        // 生成账户变动记录
        genAccountLogs();
    }

    @Override
    public void genRecord() {
        record = new Record();
        record.setGmtCreate(gmtCreate);
        record.setGmtUpdate(gmtCreate);
        record.setMemberId(member.getId());
        record.setMemberName(member.getName());
        record.setType(RecordType.pay.getCode());
        record.setStatus(RecordStatus.finished.getCode());
        record.setTotalAmount(payAmount);
        record.setRealAmount(payAmount);
        record.setGmtFinish(gmtCreate);
        record.setRemark(recordRemark);
    }

    @Override
    public void genAccountLogs() {
        this.accountLogs = new ArrayList<>();
        for (Map.Entry<Long, Long> entry : deductInfo.entrySet()) {
            Long accountId = entry.getKey();
            Optional<Account> optional = accounts.stream().filter(a -> Objects.equals(accountId, a.getId())).findAny();
            if (optional.isPresent()) {
                Account account = optional.get();
                AccountLog accountLog = new AccountLog();
                accountLog.setGmtCreate(gmtCreate);
                accountLog.setGmtUpdate(gmtCreate);
                accountLog.setAccountId(accountId);
                accountLog.setBeforeBalance(account.getBalance());
                accountLog.setAfterBalance(account.getBalance() - entry.getValue());
                accountLogs.add(accountLog);
            }
        }
    }

    public Long getPayAmount() {
        return payAmount;
    }

    public List<Account> getAccounts() {
        return accounts;
    }

    public Map<Long, Long> getDeductInfo() {
        return deductInfo;
    }

    public List<RecordAccountPay> getRecordAccountPays() {
        return recordAccountPays;
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }

    private void genDeductInfo() {
        // 可用余额从大到小排列
        accounts.sort(Comparator.comparing(Account::getBalance).reversed());
        deductInfo = new HashMap<>();
        long usedDeduct = payAmount;
        for (Account account : accounts) {
            long amount = Math.min(usedDeduct, account.getBalance());
            deductInfo.put(account.getId(), amount);
            usedDeduct = usedDeduct - amount;
            if (usedDeduct <= 0) {
                break;
            }
        }
        if (usedDeduct > 0) {
            throw new RuntimeException("余额不足！");
        }
    }

    private void genAccountPays() {
        this.recordAccountPays = new ArrayList<>(deductInfo.size());
        for (Map.Entry<Long, Long> entry : deductInfo.entrySet()) {
            RecordAccountPay recordAccountPay = new RecordAccountPay();
            recordAccountPay.setGmtCreate(gmtCreate);
            recordAccountPay.setGmtUpdate(gmtCreate);
            recordAccountPay.setAccountId(entry.getKey());
            recordAccountPay.setPayAmount(entry.getValue());
            recordAccountPay.setRealAmount(entry.getValue());
            recordAccountPays.add(recordAccountPay);
        }
    }
}
